/*
 * @Author: xiaosihan
 * @Date: 2023-05-09 00:35:17
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2025-01-28 04:13:16
 */

import {
  Camera,
  Color,
  DepthFormat,
  DepthTexture,
  LinearFilter,
  NoBlending,
  NormalBlending,
  Object3D,
  RGBAFormat,
  RawShaderMaterial,
  Scene,
  ShaderChunk,
  ShaderMaterial,
  UnsignedShortType,
  Vector2,
  WebGLRenderTarget,
  WebGLRenderer,
} from "three";
import FullScreenQuad from "./fsQuad";
import fsQuad from "./fsQuad";

// 选中对象的深度纹理
const selectDepthTexture = (() => {
  const baseDepthTexture = new DepthTexture(4096, 4096);
  baseDepthTexture.format = DepthFormat;
  baseDepthTexture.type = UnsignedShortType;
  baseDepthTexture.anisotropy = 16;
  return baseDepthTexture;
})();

// 选中对象的纹理
const selectTextureTarget = new WebGLRenderTarget(512, 512, {
  minFilter: LinearFilter,
  magFilter: LinearFilter,
  format: RGBAFormat,
  depthTexture: selectDepthTexture,
  anisotropy: 16,
});

// 深度测试后的选中纹理
const selectDethpTestTextureTarget = new WebGLRenderTarget(512, 512, {
  minFilter: LinearFilter,
  magFilter: LinearFilter,
  format: RGBAFormat,
  anisotropy: 16,
});

// 由于不能同时读写自己的纹理
const blur1TextureTarget = new WebGLRenderTarget(512, 512, {
  minFilter: LinearFilter,
  magFilter: LinearFilter,
  format: RGBAFormat,
});

// 所以需要用2个模糊纹理相互读写
const blur2TextureTarget = new WebGLRenderTarget(512, 512, {
  minFilter: LinearFilter,
  magFilter: LinearFilter,
  format: RGBAFormat,
});

const size = new Vector2();

//深度比对纹理
const depthTestShader = new ShaderMaterial({
  uniforms: {
    map: { value: null }, // 贴图纹理
    baseDepth: { value: null }, // 整个场景的深度纹理
    selectDepth: { value: null }, // 选中的对象深度纹理
  },
  vertexShader: `
        ${ShaderChunk.common}
        ${ShaderChunk.logdepthbuf_pars_vertex}
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
            ${ShaderChunk.logdepthbuf_vertex}
        }
    `,
  fragmentShader: `
        ${ShaderChunk.logdepthbuf_pars_fragment}
        uniform sampler2D map;
        uniform sampler2D baseDepth;
        uniform sampler2D selectDepth;
        varying vec2 vUv;
        void main() {
            if(texture2D(baseDepth, vUv).r < texture2D(selectDepth, vUv).r){
                discard;
            }
            if(texture2D(baseDepth, vUv).r == 1.0){
                discard;
            }
            gl_FragColor = texture2D(map, vUv);
            ${ShaderChunk.logdepthbuf_fragment}
        }
    `,
  blending: NoBlending, // 渲染时不与背景融合计算 直接替换
  depthTest: true,
  depthWrite: true,
  transparent: true,
});

// 描边着色器
const blurShader = new RawShaderMaterial({
  uniforms: {
    tDiffuse: { value: null },
    color: { value: new Color("#0f0") }, // 没有被挡住的地方的颜色绿色
    width: { value: 1.0 },
    opacity: { value: 1.0 },
    resolution: { value: new Vector2(1 / 512, 1 / 512) }, // 渲染器的分辨率
    offset: {
      value: [
        new Vector2(0.0, 1.0),
        new Vector2(0.259, 0.966),
        new Vector2(0.5, 0.866),
        new Vector2(0.707, 0.707),
        new Vector2(0.866, 0.5),
        new Vector2(0.966, 0.259),
        new Vector2(1.0, 0.0),
        new Vector2(0.966, -0.259),
        new Vector2(0.866, -0.5),
        new Vector2(0.707, -0.707),
        new Vector2(0.5, -0.866),
        new Vector2(0.259, -0.966),
        new Vector2(0.0, -1.0),
        new Vector2(-0.259, -0.966),
        new Vector2(-0.5, -0.866),
        new Vector2(-0.707, -0.707),
        new Vector2(-0.866, -0.5),
        new Vector2(-0.966, -0.259),
        new Vector2(-1.0, -0.0),
        new Vector2(-0.966, 0.259),
        new Vector2(-0.866, 0.5),
        new Vector2(-0.707, 0.707),
        new Vector2(-0.5, 0.866),
        new Vector2(-0.259, 0.966),
      ],
    },
  },
  vertexShader: `
		attribute vec3 position;
		attribute vec2 uv;
		varying vec2 vUv;
		void main() {
			vUv = uv;
			gl_Position = vec4(position, 1.0);
		}`,
  fragmentShader: `
		#ifdef GL_ES
			precision highp float;
		#endif
		uniform sampler2D tDiffuse;
		uniform vec3 color;
		uniform float width;
		uniform float opacity;
		uniform vec2 resolution;
		uniform vec2 offset[24];
		varying vec2 vUv;
		vec2 piexl = resolution * width; // 描边厚度

		vec4 stroke(){
			vec4 blurColor = vec4(0.0);
			vec4 Color = vec4(0.0);
			int vSum = 0; // 周围像素点中和 visibleColor 相似的数量
			for (int i = 0; i < 24; i++ ) {
				Color = texture2D(tDiffuse, vUv + (offset[i] * piexl));
				if (Color.a > 0.0) {
					blurColor += Color;
				}
			}
			if (blurColor.a < 0.001) {
				return vec4(0.0);
			} else {
				return vec4(color, opacity);
			}
		}
		void main() {
			vec4 Color = texture2D(tDiffuse, vUv);
			if (Color.a > 0.0 || width == 0.0) {
				gl_FragColor = Color;
			} else {
				gl_FragColor = stroke();
			}
		}`,
  blending: NoBlending, // 渲染时不与背景融合计算 直接替换
  depthTest: false,
  depthWrite: false,
  transparent: true,
});

// 过滤着色器
const filterSelectShader = new RawShaderMaterial({
  uniforms: {
    selectTexture: { value: null },
    blurTexture: { value: null },
    opacity: { value: 1 },
  },
  vertexShader: `
		attribute vec3 position;
		attribute vec2 uv;
		varying vec2 vUv;
		void main() {
			vUv = uv;
			gl_Position = vec4(position, 1.0);
		}
	`,
  fragmentShader: `
		#ifdef GL_ES
			precision highp float;
		#endif
		uniform sampler2D selectTexture;
		uniform sampler2D blurTexture;
		uniform float opacity;
		varying vec2 vUv;

		void main() {
			gl_FragColor = texture2D(blurTexture, vUv);
			gl_FragColor.a -= (texture2D(selectTexture, vUv).a);
			gl_FragColor.a *= opacity;
		}
	`,
  blending: NoBlending, // 渲染时不与背景融合计算 直接替换
  depthTest: false,
  depthWrite: false,
  transparent: true,
});

const copyShader = new ShaderMaterial({
  uniforms: {
    map: { value: null }, // 贴图纹理
  },
  vertexShader: `
        ${ShaderChunk.common}
        ${ShaderChunk.logdepthbuf_pars_vertex}
        varying vec2 vUv;
        void main() {
            vUv = uv;
            // gl_Position = vec4(position, 1.0);
            // gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
            ${ShaderChunk.logdepthbuf_vertex}
        }
    `,
  fragmentShader: `
        ${ShaderChunk.logdepthbuf_pars_fragment}
        uniform sampler2D map;
        varying vec2 vUv;
        void main() {
            gl_FragColor = texture2D(map, vUv);
            ${ShaderChunk.logdepthbuf_fragment}
        }
    `,
  depthTest: true,
  depthWrite: true,
  transparent: true,
});

// 场景对象
const scene = new Scene();

// 对象发光
export default function glow_pass(
  renderer: WebGLRenderer,
  camera: Camera,
  object3d: Object3D,
  baseDepthTexture: DepthTexture,
  devicePixelRatio: number
) {
  const {
    glow_enable = false,
    glow_color = "#04fa12",
    glow_width = 1,
    glow_opacity = 1,
  } = object3d.userData;

  if (!glow_enable || glow_width === 0) {
    return;
  }

  // 保存原帧缓冲区
  const originRenderTarget = renderer.getRenderTarget();

  // 设置选中目标的帧缓冲区
  renderer.getSize(size);
  size.multiplyScalar(devicePixelRatio);
  selectTextureTarget.setSize(size.x, size.y);
  selectDethpTestTextureTarget.setSize(size.x, size.y);
  blur1TextureTarget.setSize(size.x, size.y);
  blur2TextureTarget.setSize(size.x, size.y);

  renderer.setRenderTarget(selectTextureTarget);
  scene.children = [object3d];
  renderer.clear();
  renderer.render(scene, camera);

  // 设置描边着色器的分辨率
  blurShader.uniforms.resolution.value.set(1 / size.x, 1 / size.y);
  //描边颜色
  blurShader.uniforms.color.value.set(glow_color);
  blurShader.uniforms.width.value = glow_width / 10;

  //使用深度纹理提取选中的对象未被遮挡的像素点
  renderer.setRenderTarget(selectDethpTestTextureTarget);
  renderer.clear();
  depthTestShader.uniforms.map.value = selectTextureTarget.texture;
  depthTestShader.uniforms.baseDepth.value = baseDepthTexture;
  depthTestShader.uniforms.selectDepth.value = selectDepthTexture;
  fsQuad.render(renderer, depthTestShader);

  //一次描边
  renderer.setRenderTarget(blur1TextureTarget);
  blurShader.uniforms.tDiffuse.value = selectDethpTestTextureTarget.texture;
  blurShader.uniforms.opacity.value = 1.0;
  fsQuad.render(renderer, blurShader);

  //二次描边
  renderer.setRenderTarget(blur2TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur1TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.9;
  fsQuad.render(renderer, blurShader);

  //三次描边
  renderer.setRenderTarget(blur1TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur2TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.8;
  fsQuad.render(renderer, blurShader);

  //四次描边
  renderer.setRenderTarget(blur2TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur1TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.7;
  fsQuad.render(renderer, blurShader);

  //五次描边
  renderer.setRenderTarget(blur1TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur2TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.6;
  fsQuad.render(renderer, blurShader);

  //六次描边
  renderer.setRenderTarget(blur2TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur1TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.5;
  fsQuad.render(renderer, blurShader);

  //七次描边
  renderer.setRenderTarget(blur1TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur2TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.4;
  fsQuad.render(renderer, blurShader);

  //八次描边
  renderer.setRenderTarget(blur2TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur1TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.3;
  fsQuad.render(renderer, blurShader);

  //九次描边
  renderer.setRenderTarget(blur1TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur2TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.2;
  fsQuad.render(renderer, blurShader);

  //十次描边
  renderer.setRenderTarget(blur2TextureTarget);
  blurShader.uniforms.tDiffuse.value = blur1TextureTarget.texture;
  blurShader.uniforms.opacity.value = 0.1;
  fsQuad.render(renderer, blurShader);

  renderer.setRenderTarget(blur1TextureTarget);
  copyShader.uniforms.map.value = blur2TextureTarget.texture;
  fsQuad.render(renderer, copyShader);


  // 还原帧缓冲区 减去原始纹理保留描边
  renderer.setRenderTarget(originRenderTarget);
  filterSelectShader.uniforms.selectTexture.value = selectDethpTestTextureTarget.texture;
  filterSelectShader.uniforms.blurTexture.value = blur1TextureTarget.texture;
  filterSelectShader.uniforms.opacity.value = glow_opacity;
  filterSelectShader.blending = NormalBlending; // 与有的纹理融合计算 保留前面通道传递下来的纹理
  fsQuad.render(renderer, filterSelectShader);

  // 调试代码
  // renderer.setRenderTarget(null);
  // renderer.clearDepth();
  // copyShader.uniforms.map.value = blur2TextureTarget.texture;
  // fsQuad.render(renderer, copyShader);
  // return;
}
